package com.google.jessewilson.codesearch.util;

import java.util.IdentityHashMap;
import java.util.Map;

/**
 * A resource that needs to be disposed of when it's no longer used.
 * Users of the resource need to reserve it when they're using it, and
 * release it when they're done with it.
 *
 * @author jessewilson
 */
public interface Resource {

  /**
   * Returns a handle that allows users to reserve and release this resource.
   */
  Handle handle();

  /**
   * Release all resources allocated by this resource.
   */
  void dispose();


  /**
   * Marker interface for classes that use Results.
   */
  public interface User {
    // empty interface
  }

  /**
   * Manages references to a resource so that it can be disposed
   * when unused.
   */
  public static class Handle {

    private final Resource resource;
    private final Map<User,Void> users = new IdentityHashMap<User,Void>();
    private boolean disposed = false;

    public Handle(Resource resource) {
      this.resource = resource;
    }

    /**
     * Ensure this results is not disposed before the specified
     * user calls {@link #releaseAndDisposeIfUnused}.
     */
    public void reserve(User user) {
      if (disposed) {
        throw new IllegalStateException();
      }
      if (users.containsKey(user)) {
        throw new IllegalArgumentException();
      }
      users.put(user, null);
    }

    /**
     * Cancel the {@link #reserve reservation} for this user.
     */
    public void releaseAndDisposeIfUnused(User user) {
      if (!users.containsKey(user)) {
        throw new IllegalArgumentException();
      }
      users.remove(user);
      if (users.isEmpty()) {
        disposed = true;
        resource.dispose();
      }
    }
  }
}
